<?php

namespace App\Jobs;

use App\Common\Libs\ArrayHelper;
use App\Common\Libs\IdBuilder;
use App\Common\Libs\Json;
use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Foundation\Bus\PendingDispatch;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Log;

/**
 * Class BaseJob
 * @package App\Jobs
 */
class BaseJob implements ShouldQueue
{

    /**
     * trait
     */
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable;

    /**
     * @var string $id
     */
    protected static $id;

    /**
     * @var array $params
     */
    protected $params;

    /**
     * Create a new job instance.
     *
     * @param array $params
     * @param string $id
     */
    public function __construct($params = [], $id = '')
    {
        $this->params = $params;
        self::$id = $id ? : (self::$id ? : 0);
    }

    /**
     * Dispatch the job with the given arguments.
     *
     * @param $params
     * @param string $driver
     * @param int $delay
     * @return \Illuminate\Foundation\Bus\PendingDispatch
     */
    public static function dispatch($params, $driver = 'queue', $delay = 0)
    {
        $id = self::$id ?: 0;
        $data = ArrayHelper::merge(['id' => $id, 'data' => $params], ['driver' => $driver]);
        Log::channel('job')->info("[JOB-DIP] " . static::class . " : " . json_encode($data));
        if ('queue' == $driver) {
            return (new PendingDispatch(new static($params, $id)))->delay($delay);
        } elseif ('sync' == $driver) {
            return self::dispatchSync($params, $id);
        }
        return self::dispatchNow($params, $id);
    }

    /**
     * Dispatch a command to its appropriate handler in the current process.
     *
     * Queuable jobs will be dispatched to the "sync" queue.
     *
     * @return mixed
     */
    public static function dispatchSync()
    {
        return app(Dispatcher::class)->dispatchSync(new static(...func_get_args()));
    }

    /**
     * Dispatch a command to its appropriate handler in the current process.
     *
     * @return mixed
     */
    public static function dispatchNow()
    {
        return app(Dispatcher::class)->dispatchNow(new static(...func_get_args()));
    }

    /**
     * Batch dispatch
     * @param array $params
     * @return \Illuminate\Bus\Batch|int
     */
    public static function dispatchAll(array $params)
    {
        $id = self::$id = IdBuilder::getUniqueID();
        $data = ArrayHelper::merge(['id' => $id, 'data' => ['jobs_first' => $params[0] ?? [], 'jobs_count' => count($params)]], ['driver' => 'batch']);
        Log::channel('job')->info("[JOB-DIP] " . static::class . " : " . json_encode($data));
        try {
            $jobs = [];
            foreach ($params as $param) {
                $jobs[] = new static($param, $id);
            }
            $result =  Bus::batch($jobs)->dispatch();
        } catch (\Throwable $exception) {
            $message = $exception->getMessage();
            $data = ArrayHelper::merge(['id' => $id, 'data' =>['jobs_first' => $params[0] ?? [], 'jobs_count' => count($params)]], ['message' => $message]);
            Log::channel('job')->info("[JOB-FAL] " . static::class . " : " . json_encode($data));
            $result = 0;
        }
        return $result;
    }

    /**
     * Execute the job.
     *
     * @return int
     */
    public function handle()
    {
        $id = self::$id = IdBuilder::getUniqueID();
        $data = ArrayHelper::merge(['id' => $id], ['data' => $this->params]);
        Log::channel('job')->info("[JOB-HDL] " . static::class . " : " . json_encode($data));
        $result = (int)$this->handler();
        $data = ArrayHelper::merge(['id' => $id], ['result' => $result]);
        Log::channel('job')->info("[JOB-RES] " . static::class . " : " . json_encode($data));
        return $result;
    }

    /**
     * Failed jobs
     *
     * @param $exception
     * @param int $id
     * @return bool
     */
    public function failed($exception, $id = 0)
    {
        $id = self::$id ?: $id;
        $message = 'ERROR';
        if (method_exists($exception, 'getMessage')) {
            $message = $exception->getMessage();
        }
        $data = ArrayHelper::merge(['id' => $id, 'data' => $this->params], ['message' => $message]);
        Log::channel('job')->info("[JOB-FAL] " . static::class . " : " . json_encode($data));
        return true;
    }

    /**
     * Get params
     *
     * [!] 获取参数方法
     * @return array
     */
    public function getParams()
    {
        return $this->params;
    }

    /**
     * Job handler
     *
     * [!] 子类继承处理逻辑使用此方法
     * @return int
     */
    public function handler()
    {
        $params = $this->getParams();

        // 判断是否带有 action 参数，可以直接返回处理
        if (isset($params['action']) && $params['action']) {
            try {
                $method = $params['action'];
                $data = $this->_paramsFormat($params['json']);
                $result = $this->$method($data);
            } catch (\Exception $e) {
                $this->failed($e, self::$id);
                $result = 0;
            }
            return $result;
        }

        return 1;
    }

    /**
     * 参数格式化
     * @param string $json
     * @return array
     */
    private function _paramsFormat(string $json): array
    {
        $data = Json::decode($json, true) ?: [];
        return [
            'msgContent' => $data,
        ];
    }

}
